using System;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using Gtk;

using Dashboard;
using Dashboard.Util;

namespace Dashboard.Sidebar
{
	public abstract class Tile : Button, IComparable<Tile>
	{
		private Image image = new Image();
		private Label label = new Label();		
		private string text;	 

		private Match match;
		private DateTime created = DateTime.Now;
		private ulong window;

		public Match Match 
		{
			get { return match; }
		}
		
		public DateTime Created
		{
			get { return created; }
		}

		public ulong Window
		{
			get { return window; }
			set { window = value; }
		}

		public Tile ()
		{
			HBox box = new HBox ();
			box.Spacing = 5;
			box.PackStart (image, false, false, 0);
			box.PackStart (label, true, true, 0);
			box.ShowAll ();
			Add (box);
			
			label.Ellipsize = Pango.EllipsizeMode.End;
			Relief = ReliefStyle.None;
		}

		public Tile (Match match) : this ()
		{
			this.match = match;
		}
		
		public string Text {
			get { return text; }
			set {
				text = value;
				label.Text = value;
			}
		}
		
		public Gdk.Pixbuf Icon {
			get { return image.Pixbuf; }
			set { 
				if(value.Width <= pixbuf_size && value.Height <= pixbuf_size) {
					image.Pixbuf = value;
					return;
				}
				
				image.Pixbuf = value.ScaleSimple(pixbuf_size, 
												 pixbuf_size,
												 Gdk.InterpType.Bilinear);
			}
		}

		public int CompareTo (Tile other)
		{
			return other.Match.Relevancy.CompareTo (match.Relevancy);
		}

		private static Dictionary<string, Type> tiles = new Dictionary <string, Type> ();

		static Tile () 
		{
			List<Type> tile_types = ReflectionFu.ScanAssemblyForClass (Assembly.GetExecutingAssembly(), typeof (Tile));
			
			foreach (Type tile_type in tile_types) { 
				object [] attributes = Attribute.GetCustomAttributes (tile_type, typeof (TileAttribute));
				
				foreach (object attribute in attributes) {
					TileAttribute tile_attribute = attribute as TileAttribute;
					
					if (tile_attribute == null)
						continue;
					
					tiles.Add (tile_attribute.Type, tile_type);
				}
			}
		}

		public static Tile BuildTile (Match match)
		{
			if (tiles.ContainsKey (match.Type)) {
				try {
					return System.Activator.CreateInstance (tiles [match.Type], new object[] {match}) as Tile;
				} catch (Exception ex) {
					Console.WriteLine ("Caught exception while instantiating tile: {0}", match.Type);
					return null;
				}
			} else {
				return null;
			}
		}

		[DllImport ("libgtk-x11-2.0.so.0")]
		private static extern IntPtr gtk_icon_theme_get_default ();
		
		[DllImport ("libgtk-x11-2.0.so.0")]
		private static extern IntPtr gtk_icon_theme_lookup_icon (IntPtr theme, string name, int size, int flags, IntPtr error);
		
		[DllImport ("libgtk-x11-2.0.so.0")]
		private static extern int gtk_icon_info_get_base_size (IntPtr icon_info);
		
		[DllImport ("libgtk-x11-2.0.so.0")]
		private static extern void gtk_icon_info_free (IntPtr icon_info);
		
		[DllImport ("libgtk-x11-2.0.so.0")]
		private static extern IntPtr gtk_icon_theme_load_icon (IntPtr theme, string name, int size, int flags, IntPtr error);

		private static readonly int pixbuf_size = 24;
		
		protected static Gdk.Pixbuf LoadThemeIcon (string name, int size)
		{
			try {
				IntPtr info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (), name, size, 0, IntPtr.Zero);
				if (info == IntPtr.Zero)
					return null;
				
				int base_size = gtk_icon_info_get_base_size (info);
				
				// If the icon has no base size, or it would be huge
				// compared to what we asked for, scale it down.
				// 1.33334 is pretty arbitrary, roughly calculated
				// as the difference between icon sizes 48 and 64.
				if (base_size == 0 || base_size > size * 1.33334)
					base_size = size;
				gtk_icon_info_free (info);
				
				IntPtr native = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), name, base_size, 0, IntPtr.Zero);
				if (native != IntPtr.Zero) {
					Gdk.Pixbuf ret = (Gdk.Pixbuf) GLib.Object.GetObject(native, true);
                                        return ret;
				}
			} catch (System.Exception e) {
				System.Console.Write (e.ToString ());
			}
			return null;
		}
		
		protected static Gdk.Pixbuf LoadMimeIcon (string mimetype, int size)
		{
			Gtk.IconTheme icon_theme = Gtk.IconTheme.Default;
			Gnome.IconLookupResultFlags result;
			
			// FIXME when ximian bug #76540 is fixed
			// change "new Gnome.Vfs.FileInfo (IntPtr.Zero)" to "null"
			string icon_name = Gnome.Icon.Lookup (icon_theme, null, null, null, 
												  new Gnome.Vfs.FileInfo (IntPtr.Zero), mimetype, 
												  (Gnome.IconLookupFlags) 0, out result);
			
			if (icon_name == null)
				return null;
			
			Gtk.IconInfo icon_info = icon_theme.LookupIcon (icon_name, size, 0);
			
			if (icon_info == null)
				return null;
			try {
				return icon_info.LoadIcon ();
			} catch (System.Exception e) {
				System.Console.Write (e.ToString ());
			}
			
			return null;
		}
	}
}
